#include <d3dx9.h>

#include "ArcBall.h"
#include "ThreeDAxis.h"

LPDIRECT3D9             g_pD3D				= NULL ; // Used to create the D3DDevice
LPDIRECT3DDEVICE9       g_pd3dDevice		= NULL ; // Our rendering device
ID3DXMesh*				g_pMesh				= NULL ; // Hold the sphere
D3DXMATRIX world ;		// world matrix for model

ArcBall arcball ;
bool DragSinceLastUpdate = false ;

// variable handle in shader file
D3DXHANDLE ViewProjMatrixHanle = 0 ;

// initilize a 3D axis
ThreeDAxis axis;

HRESULT InitD3D( HWND hWnd )
{
	// Create the D3D object, which is needed to create the D3DDevice.
	if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
		return E_FAIL;

	D3DPRESENT_PARAMETERS d3dpp; 
	ZeroMemory( &d3dpp, sizeof(d3dpp) );
	d3dpp.Windowed = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;

	// Create device
	if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
		D3DCREATE_SOFTWARE_VERTEXPROCESSING,
		&d3dpp, &g_pd3dDevice ) ) )
	{
		return E_FAIL;
	}

	// wireframe mode
	g_pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME) ;

	// Turn off culling, so we see the front and back of the triangle   
	g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ); 

	// Disable lighting
	g_pd3dDevice->SetRenderState( D3DRS_LIGHTING , FALSE );   

	D3DXMatrixIdentity(&world) ;

	// set view
	D3DXVECTOR3 eyePt(0.0f, 0.0f, -5.0f) ;
	D3DXVECTOR3 upVec(0.0f, 1.0f, 0.0f) ;
	D3DXVECTOR3 lookCenter(0.0f, 0.0f, 0.0f) ;

	D3DXMATRIX view ;
	D3DXMatrixLookAtLH(&view, &eyePt, &lookCenter, &upVec) ;
	g_pd3dDevice->SetTransform(D3DTS_VIEW, &view) ;

	// set projection
	D3DXMATRIX proj ;
	D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI / 4, 1.0f, 1.0f, 1000.0f) ;
	g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &proj) ;

	D3DXCreateTeapot(g_pd3dDevice, &g_pMesh, NULL) ;

	axis.SetDevice(g_pd3dDevice) ;
	axis.InitiVertex() ;
	return S_OK;
}

VOID Cleanup()
{
	if( g_pd3dDevice != NULL) 
		g_pd3dDevice->Release();

	if( g_pD3D != NULL)
		g_pD3D->Release();

	if(g_pMesh != NULL)
		g_pMesh->Release() ;
}

void SetupMatrix()
{
	//D3DXVECTOR3 axis(0.0f, 0.0f, 1.0f) ;
	//float angle = cosf(D3DX_PI / 4) ;

	//D3DXQUATERNION q(0.0f, 0.0f, sinf(D3DX_PI / 4), angle) ;

	//D3DXMATRIX rotMatrix ;
	////D3DXMatrixRotationQuaternion(&rotMatrix, &q) ;

	//D3DXMatrixRotationAxis(&rotMatrix, &axis, D3DX_PI / 2) ;
	//g_pd3dDevice->SetTransform(D3DTS_WORLD, &rotMatrix) ;

	
	if(DragSinceLastUpdate)
	{
		// Get the rotation matrix from arcball
		D3DXMATRIX currentRot ;
		D3DXMatrixRotationQuaternion(&currentRot, &arcball.GetRotationQuatIncreament()) ;

		world *= currentRot ;

		// Update the world matrix of teapot
		g_pd3dDevice->SetTransform(D3DTS_WORLD, &world) ;
	}
}

VOID Render()
{
	SetupMatrix() ;

	// Clear the back-buffer to a RED color
	g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0 );

	// Begin the scene
	if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
	{

		axis.Draw() ;
		//g_pMesh->DrawSubset(0) ;
		// End the scene
		g_pd3dDevice->EndScene();
	}

	// Present the back-buffer contents to the display
	g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}

LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
	switch( msg )
	{
	case WM_LBUTTONDOWN:
		DragSinceLastUpdate = true ;
		break ;

	case WM_LBUTTONUP:
		DragSinceLastUpdate = false ;
		break ;

	case WM_KEYDOWN:
		{
			switch( wParam )
			{
			case VK_ESCAPE:
				SendMessage( hWnd, WM_CLOSE, 0, 0 );
				break ;
			default:
				break ;
			}
		}
		break ;

	case WM_DESTROY:
		Cleanup();
		PostQuitMessage( 0 );
		return 0;
	}

	arcball.HandleMessages(hWnd, msg, wParam, lParam) ;

	return DefWindowProc( hWnd, msg, wParam, lParam );
}

INT WINAPI wWinMain( HINSTANCE hInst, HINSTANCE, LPWSTR, INT )
{
	// Register the window class
	WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, 
		GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
		L"D3D Tutorial", NULL };
	RegisterClassEx( &wc );

	// Create the application's window
	HWND hWnd = CreateWindow( L"D3D Tutorial", L"My first shader", 
		WS_OVERLAPPEDWINDOW , 0, 0, 500, 500,
		NULL, NULL, wc.hInstance, NULL );

	// Initialize Direct3D
	if( SUCCEEDED( InitD3D( hWnd ) ) )
	{ 
		// Show the window
		ShowWindow( hWnd, SW_SHOWDEFAULT );
		UpdateWindow( hWnd );

		// Enter the message loop
		MSG    msg ; 
		ZeroMemory( &msg, sizeof(msg) );
		PeekMessage( &msg, NULL, 0U, 0U, PM_NOREMOVE );

		while (msg.message != WM_QUIT)  
		{
			if( PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE) != 0)
			{
				TranslateMessage (&msg) ;
				DispatchMessage (&msg) ;
			}
			else // Render the game if no message to process
			{
				Render() ;
			}
		}
	}

	UnregisterClass( L"D3D Tutorial", wc.hInstance );
	return 0;
}



